using System.Collections.Generic;
using UnityEngine;
using System;
using Codice.CM.Common.Purge;
using Framework.Save;
using LitJson;
using Mono.Cecil;

namespace GameCore
{
    [Serializable]
    public class InventoryData : GameData
    {
        public ResourceID id;
        public Shape shape;
        public Shape occupation = new();
        private readonly Dictionary<Vector2Int, ItemData> grid2item = new();
        private readonly Dictionary<ItemData, Vector2Int> item2center = new();
        private readonly Dictionary<ItemData, Shape> item2shape = new();

        public InventoryData(ResourceID id, Shape shape)
        {
            this.id = id;
            this.shape = shape;
        }

        public InventoryData(JsonData json) : base(json) { }

        public bool HasSpace(Shape shape)
        {
            return this.shape.Contains(shape) && occupation.Overlap(shape) == 0;
        }

        public ItemData GetItem(Vector2Int gridPos)
        {
            return grid2item.GetValueOrDefault(gridPos, null);
        }

        public void PlaceItem(Vector2Int gridPos, ItemData itemData, Shape shape)
        {
            var offsetShape = shape.Clone().Translate(gridPos);
            item2shape[itemData] = offsetShape;
            item2center[itemData] = gridPos;
            foreach (var cell in offsetShape)
                grid2item[cell] = itemData;
            occupation.AddShape(offsetShape);
        }

        public void RemoveItem(Vector2Int gridPos)
        {
            var item = GetItem(gridPos);
            if (item == null) return;
            var offsetShape = item2shape[item];
            item2shape.Remove(item);
            item2center.Remove(item);
            foreach (var cell in offsetShape)
            {
                grid2item.Remove(cell);
            }
            occupation.RemoveShape(offsetShape);
        }

        public class ItemSearchResult
        {
            public ResourceID id;
            public Vector2Int gridPos;
            public Shape shape;
            public ItemData data;
        }

        public List<ItemSearchResult> GetAllItems()
        {
            var ret = new List<ItemSearchResult>();
            foreach (var (inventoryItem, _) in item2shape)
            {
                ret.Add(new ItemSearchResult()
                {
                    id = inventoryItem.ItemID,
                    data = inventoryItem,
                    gridPos = item2center[inventoryItem],
                    shape = item2shape[inventoryItem]
                });
            }
            return ret;
        }

        public List<ItemSearchResult> FindItems(ItemData itemData, ItemCompareMode compareMode = ItemCompareMode.ID)
        {
            var ret = new List<ItemSearchResult>();
            foreach (var (inventoryItem, _) in item2shape)
            {
                if (inventoryItem.Match(itemData, compareMode))
                {
                    ret.Add(new ItemSearchResult()
                    {
                        id = inventoryItem.ItemID,
                        data = inventoryItem,
                        gridPos = item2center[inventoryItem],
                        shape = item2shape[inventoryItem]
                    });
                }
            }
            return ret;
        }

        public ItemSearchResult FindItem(ItemData itemData, ItemCompareMode compareMode = ItemCompareMode.ID)
        {
            foreach (var (inventoryItem, _) in item2shape)
            {
                if (!inventoryItem.Match(itemData, compareMode)) continue;
                return new ItemSearchResult()
                {
                    id = inventoryItem.ItemID,
                    data = inventoryItem,
                    gridPos = item2center[inventoryItem],
                    shape = item2shape[inventoryItem]
                };
            }
            return null;
        }

        public ItemSearchResult GetCenter(ItemData itemData)
        {
            return new ItemSearchResult()
            {
                id = itemData?.ItemID ?? new ResourceID("", ""),
                data = item2center.ContainsKey(itemData) ? itemData : null,
                gridPos = item2center.GetValueOrDefault(itemData, Vector2Int.zero),
                shape = item2shape.GetValueOrDefault(itemData, null)
            };
        }
        
        public bool IsEmpty()
        {
            return occupation.Size == 0;
        }

        public bool IsFull()
        {
            return occupation.Size == shape.Size;
        }

        public override JsonData Serialize()
        {
            var json = JsonUtil.NewObject<InventoryData>();
            json["inventory_id"] = id.ToString();
            json["shape"] = shape.Serialize();
            var items = JsonUtil.NewArray();
            foreach (var (item, gridPos) in item2center)
            {
                var temp = JsonUtil.NewObject();
                temp["item"] = item.Serialize();
                temp["shape"] = item2shape[item].Serialize();
                temp["x"] = gridPos.x;
                temp["y"] = gridPos.y;
                items.Add(temp);
            }
            json["items"] = items;
            return json;
        }

        public override void Deserialize(JsonData json)
        {
            id = (string)json["inventoryID"];
            shape = new Shape();
            shape.Deserialize(json["shape"]);
            foreach (JsonData item in json["items"])
            {
                var gridPos = new Vector2Int((int)item["x"], (int)item["y"]);
                var itemShape = new Shape();
                itemShape.Deserialize(item["shape"]);
                var itemData = new ItemData(item["item"]);
                PlaceItem(gridPos, itemData, itemShape);
            }
        }

        
    }
}